package com.tutou.ikgroup.websocket;

import cn.hutool.core.bean.BeanUtil;
import com.tutou.ikgroup.mapper.ChatFriendMapper;
import com.tutou.ikgroup.mapper.ChatInitMapper;
import com.tutou.ikgroup.mapper.ChatMapper;
import com.tutou.ikgroup.pojo.dto.ChatDTO;
import com.tutou.ikgroup.pojo.dto.FriendDTO;
import com.tutou.ikgroup.pojo.po.Chatroom;
import com.tutou.ikgroup.pojo.po.Friend;
import com.tutou.ikgroup.pojo.po.Message;
import com.tutou.ikuser.mapper.UserMapper;
import com.tutou.ikuser.pojo.dto.Result;
import com.tutou.ikuser.pojo.dto.UserDTO;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.GlobalEventExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import io.netty.channel.Channel;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import static com.tutou.ikuser.util.RedisConstants.*;

@Component
public class ChannelContextUtil {
    private static final Logger logger = LoggerFactory.getLogger(ChannelContextUtil.class);

    @Autowired
    private ChatMapper chatMapper;

    @Autowired
    private ChatFriendMapper chatFriendMapper;

    @Autowired
    private ChatInitMapper chatInitMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    //单聊
    private static final ConcurrentHashMap<String, Channel> USER_CONTEXT_MAP = new ConcurrentHashMap<>();

    //群聊
    private static final ConcurrentHashMap<String, ChannelGroup> GROUP_CONTEXT_MAP = new ConcurrentHashMap<>();

    //记录Channel与用户id的对应关系，方便服务端发送消息给指定用户
    public void addContext(String userId, Channel channel) {
        //channel原本的channelId，每一次连接都会生成一个channelId
        String channelId = channel.id().toString();

        //在Netty中，AttributeKey 是用于在 Channel、ChannelHandlerContext 或其他 Netty 组件中存储和访问属性的关键类。
        //它允许开发者将自定义的属性与 Netty 的组件关联起来，以便在处理器链中传递和共享数据。
        AttributeKey attributeKey = null;

        //如果AttributeKey内不存在channelId属性，就创建一个channelId属性
        if (AttributeKey.exists(channelId)) {

            //新建一个channelId属性
            attributeKey = AttributeKey.newInstance(channelId);
        } else {
            //获取channelId属性
            attributeKey = AttributeKey.valueOf(channelId);
        }

        //将channelId与userId关联起来
        channel.attr(attributeKey).set(userId);

        //将用户channel放进单聊集合中
        USER_CONTEXT_MAP.put(userId, channel);
        logger.info("id为" + userId + "的用户上线");
        //加载用户聊群
        List<String> chatroomIdListByUserId = chatMapper.getChatroomIdListByUserId(userId);
        //创建属于该用户的聊群的channelGroup，并将该用户channel放进群聊集合
        for (String roomId : chatroomIdListByUserId) {
            //判断该聊群是否创建
            ChannelGroup channelGroup = GROUP_CONTEXT_MAP.get(roomId);
            if (channelGroup == null) {
                GROUP_CONTEXT_MAP.put(roomId, new DefaultChannelGroup(GlobalEventExecutor.INSTANCE));
            }
            GROUP_CONTEXT_MAP.get(roomId).add(channel);
        }
    }

    //单聊
    public Result sendMessageOBO(String token, ChatDTO chatDTO) {
        //将信息存到redis中
        String senderName = chatDTO.getSenderName();
        String receiverId = chatDTO.getReceiverId();
        if (receiverId == null) {
            return Result.fail("请选择用户~");
        }
        Channel channel = USER_CONTEXT_MAP.get(receiverId);
        if (channel != null) {
            channel.writeAndFlush(new TextWebSocketFrame
                    (senderName + "：" + chatDTO.getMessage()));
        }
        return Result.ok("发送成功~");
    }


    //创建聊群
    public Result creatChatroom(ChatDTO chatDTO) {
        //先创建一个ChannelGroup
        String chatroomId = chatDTO.getChatroom().getChatroomId();
        ChannelGroup group = GROUP_CONTEXT_MAP.get(chatroomId);
        if (group == null) {
            //创建一个channelGroup，并将其放进channelGroup集合中
            group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
            GROUP_CONTEXT_MAP.put(chatroomId, group);
        }
        //将创建者加入channelGroup中
        group.add(USER_CONTEXT_MAP.get(chatDTO.getSenderId()));
        group.writeAndFlush(new TextWebSocketFrame("您刚刚创建了“" + chatDTO.getChatroom().getRoomName() + "”聊群"));
        return Result.ok("创建成功~");
    }

    //邀请用户进群
    public Result inviteUser(ChatDTO chatDTO) {
        ChannelGroup group = GROUP_CONTEXT_MAP.get(chatDTO.getChatroom().getChatroomId());
        //将被邀请者加入channelGroup中
        //判断被邀请者是否在线
        Channel channel = USER_CONTEXT_MAP.get(chatDTO.getReceiverId());
        if (channel != null) {
            group.add(channel);
            group.writeAndFlush(new TextWebSocketFrame("欢迎加入" + chatDTO.getChatroom().getRoomName()));
        }
        return Result.ok("已发送邀请~");
    }

    //群聊发送信息
    public Result sendMessageChatroom(ChatDTO chatDTO) {
        String chatroomId = chatDTO.getChatroom().getChatroomId();
        if (chatroomId == null) {
            return Result.fail("请选择群聊~");
        }
        ChannelGroup channelGroup = GROUP_CONTEXT_MAP.get(chatroomId);
        if (channelGroup == null) {
            return Result.fail("群聊不存在~");
        }
        //发布信息
        channelGroup.writeAndFlush(new TextWebSocketFrame
                (userMapper.selectUser(chatDTO.getSenderId()).getUserName() + "：" + chatDTO.getMessage()));
        return Result.ok("发送成功~");
    }

    //下线
    public void removeContext(Channel channel) {
        String userId = channel.attr(AttributeKey.valueOf(channel.id().toString())).get().toString();
        //记录用户下线时间
        chatInitMapper.updateOfflineTime(userId);
        //存进redis中
        stringRedisTemplate.opsForValue().set(USER_LOGOUT_KEY + userId,
                String.valueOf(LocalDateTime.now().toEpochSecond(ZoneOffset.UTC) * 1000));
        USER_CONTEXT_MAP.remove(channel.id().toString());
        List<String> chatroomIdListByUserId = chatMapper.getChatroomIdListByUserId(userId);
        for (String roomId : chatroomIdListByUserId) {
            GROUP_CONTEXT_MAP.get(roomId).remove(channel);
        }
    }

    //发送好友请求
    public Result addFriend(FriendDTO friendDTO) {
        String receiverId = friendDTO.getReceiverId();
        Channel receiver = USER_CONTEXT_MAP.get(receiverId);
        if (receiver != null) {
            USER_CONTEXT_MAP.get(receiverId)
                    .writeAndFlush(new TextWebSocketFrame(friendDTO.getSenderName() + "向你发送一条好友请求:"
                            + friendDTO.getMessage()));
        }
        return Result.ok("发送成功~");
    }

    //好友申请同意或拒绝
    public Result passOrRejectFriend(FriendDTO friendDTO) {
        Short status = friendDTO.getStatus();
        Channel channel = USER_CONTEXT_MAP.get(friendDTO.getReceiverId());
        //添加好友信息，好友同意、拒绝后会有两条好友信息，一条是发送邀请的好友信息，一条是被邀请的好友信息
        if (channel != null) {
            //在线
            if (status == 3) {
                channel.writeAndFlush(new TextWebSocketFrame(friendDTO.getSenderName() + "接受了你的好友申请~"));
                return pass(friendDTO);
            } else {
                channel.writeAndFlush(new TextWebSocketFrame(friendDTO.getSenderName() + "拒绝了你的好友申请~"));
                return reject(friendDTO);
            }
        } else {
            //离线
            if (status == 3) {
                return pass(friendDTO);
            } else {
                return reject(friendDTO);
            }
        }
    }

    private Result pass(FriendDTO friendDTO) {
        friendDTO.setMessage(friendDTO.getSenderName() + "接受了你的好友申请~");
        //chatMapper.saveMessage(FriendDTO.toChatDto(friendDTO));
        //记录被添加用户拒绝添加用户的好友请求
        chatFriendMapper.saveFriend2(friendDTO);
        return Result.ok("已接受" + friendDTO.getReceiverName() + "好友申请~");
    }

    private Result reject(FriendDTO friendDTO) {
        friendDTO.setMessage(friendDTO.getSenderName() + "拒绝了你的好友申请~");
        //chatMapper.saveMessage(FriendDTO.toChatDto(friendDTO));
        //记录被添加用户拒绝添加用户的好友请求
        chatFriendMapper.saveFriend2(friendDTO);
        return Result.ok("已拒绝" + friendDTO.getReceiverName() + "好友申请~");
    }

    //好友申请通知
    public void getNewFriend(String userId, Map<String, String> friends) {
        friends.forEach((sender, message) -> {
            USER_CONTEXT_MAP.get(userId).writeAndFlush(new TextWebSocketFrame(sender + "向您发送了好友请求~ 请求信息：" + message));
        });
    }

    //提示用户有多少条单聊信息未读
    public Result getOfflineInfo(String userId, Map<String, Long> chatOfflineInfo) {
        chatOfflineInfo.forEach((sender, count) -> {
            USER_CONTEXT_MAP.get(userId)
                    .writeAndFlush(new TextWebSocketFrame("用户：" + sender + "有" + count + "条信息未读~"));
        });
        return Result.ok("离线单聊信息~");
    }

    //提示用户有多少条群聊信息未读
    public Result getOfflineInfoChatroom(String userId, Map<String, Long> offlineInfos) {
        offlineInfos.forEach((chatroom, count) -> {
            USER_CONTEXT_MAP.get(userId)
                    .writeAndFlush(new TextWebSocketFrame("群聊：" + chatroom + "有" + count + "条信息未读~"));
        });
        return Result.ok("离线群聊信息~");
    }

    //展示在下线时候发送的好友申请情况（同意、拒绝）
    public void getNewFriendPassOrReject(String userId, List<String> newFriendPassOrReject) {
        newFriendPassOrReject.forEach(POR -> {
            USER_CONTEXT_MAP.get(userId)
                    .writeAndFlush(new TextWebSocketFrame(POR));
        });
    }

    public Result oboMessage(String senderId, List<Message> oboMessage) {
        oboMessage.forEach(message -> {
            USER_CONTEXT_MAP.get(senderId)
                    .writeAndFlush(new TextWebSocketFrame(message.getSenderName() + ":" + message.getContent()));
        });
        return Result.ok("单聊信息~");
    }

    public Result deleteFriend(FriendDTO friendDTO) {
        USER_CONTEXT_MAP.get(friendDTO.getSenderId())
                .writeAndFlush(new TextWebSocketFrame("您已删除用户" + friendDTO.getReceiverName() + "~"));
        return Result.ok("您已删除用户" + friendDTO.getReceiverName() + "~");
    }

    public Result exitChatroom(String chatroomName, ChatDTO chatDTO) {
        USER_CONTEXT_MAP.get(chatDTO.getSenderId()).writeAndFlush(new TextWebSocketFrame("已退出聊群：" + chatroomName));
        return Result.ok("退出成功~");
    }

    public Result deleteChatroom(String chatroomName, ChatDTO chatDTO) {
        USER_CONTEXT_MAP.get(chatDTO.getSenderId()).writeAndFlush(new TextWebSocketFrame("聊群：" + chatroomName + "已解散~"));
        return Result.ok();
    }
}
